package org.jflame.context.mybatis;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.jflame.commons.config.ConfigHolder;
import org.jflame.commons.crypto.AsymmetricEncryptor;
import org.jflame.commons.crypto.BaseEncryptor;
import org.jflame.commons.crypto.CipherArgs;
import org.jflame.commons.crypto.DelegateEncryptor;
import org.jflame.commons.crypto.EncryptException;
import org.jflame.commons.crypto.SymmetricEncryptor;
import org.jflame.commons.crypto.Transformation;
import org.jflame.commons.util.StringHelper;

/**
 * 加密保存字段处理器
 * 
 * @author charles.zhang
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
public class EncryptTextTypeHandler extends BaseTypeHandler<String> {

    private final DelegateEncryptor cipherer;

    public EncryptTextTypeHandler() {
        String tf = ConfigHolder.getString("mybatis.encryptor.alg");
        if (StringHelper.isEmpty(tf)) {
            throw new RuntimeException("未配置字段加密算法:mybatis.encryptor.alg");
        }
        Transformation transformation = Transformation.parse(tf);
        BaseEncryptor encryptor = null;
        CipherArgs args = null;
        try {
            if (transformation.getAlg()
                    .isSymmetric()) {
                encryptor = new SymmetricEncryptor(transformation);
                String key = ConfigHolder.getString("mybatis.encryptor.key");
                if (StringHelper.isEmpty(key)) {
                    throw new RuntimeException(tf + "需要配置密钥:mybatis.encryptor.key");
                }
                args = CipherArgs.buildSym(transformation.getAlg(), key);
            } else {
                encryptor = AsymmetricEncryptor.of(transformation);
                String publicKey = ConfigHolder.getString("mybatis.encryptor.public_key");
                String privateKey = ConfigHolder.getString("mybatis.encryptor.private_key");
                args = CipherArgs.buildAsym(transformation.getAlg(), publicKey, privateKey);
            }
        } catch (EncryptException e) {
            throw new RuntimeException(e);
        }
        if (transformation.isNeedIV()) {
            String ivStr = ConfigHolder.getString("mybatis.encryptor.iv");
            if (StringHelper.isEmpty(ivStr)) {
                throw new RuntimeException(tf + "需要配置向量IV:mybatis.encryptor.iv");
            }
            args.setIv(ivStr.getBytes());
        }
        cipherer = new DelegateEncryptor(encryptor, args);
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
            throws SQLException {
        if (StringHelper.isNotEmpty(parameter)) {
            try {
                ps.setString(i, cipherer.encrypt(parameter));
            } catch (EncryptException e) {
                e.printStackTrace();
                ps.setString(i, parameter);
            }
        } else {
            ps.setString(i, parameter);
        }
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String textValue = rs.getString(columnName);
        return dencryptColumnValue(textValue);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String textValue = rs.getString(columnIndex);
        return dencryptColumnValue(textValue);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String textValue = cs.getString(columnIndex);
        return dencryptColumnValue(textValue);
    }

    private String dencryptColumnValue(String columnValue) {
        if (StringHelper.isEmpty(columnValue)) {
            return columnValue;
        }
        try {
            return cipherer.decrypt(columnValue);
        } catch (EncryptException e) {
            e.printStackTrace();
            return columnValue;
        }
    }
}
